/*
 *   Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "CCITTChecksumReverse.h"
#include "SoundCardController.h"
#include "DStarDefines.h"

// #define	AUDIO_LOOPBACK

const unsigned int BUFFER_LENGTH = 200U;

const unsigned int  NIBBLE_BITS[] = {0U, 1U, 1U, 2U, 1U, 2U, 2U, 3U, 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U};

const unsigned int DV_FRAME_LENGTH_BITS = DV_FRAME_LENGTH_BYTES * 8U;

const unsigned int MAX_SYNC_BITS = 50U * DV_FRAME_LENGTH_BITS;

const unsigned int FEC_SECTION_LENGTH_BITS = 660U;

// D-Star bit order version of 0x55 0x55 0x6E 0x0A
const wxUint32     FRAME_SYNC_DATA = 0x00557650U;
const wxUint32     FRAME_SYNC_MASK = 0x00FFFFFFU;
const unsigned int FRAME_SYNC_ERRS = 2U;

// D-Star bit order version of 0x55 0x2D 0x16
const wxUint32     DATA_SYNC_DATA = 0x00AAB468U;
const wxUint32     DATA_SYNC_MASK = 0x00FFFFFFU;
const unsigned int DATA_SYNC_ERRS = 2U;

// D-Star bit order version of 0x55 0x55 0xC8 0x7A
const wxUint32     END_SYNC_DATA = 0xAAAA135EU;
const wxUint32     END_SYNC_MASK = 0xFFFFFFFFU;
const unsigned int END_SYNC_ERRS = 3U;

const unsigned char BIT_SYNC    = 0xAAU;

const unsigned char FRAME_SYNC0 = 0xEAU;
const unsigned char FRAME_SYNC1 = 0xA6U;
const unsigned char FRAME_SYNC2 = 0x00U;

const unsigned char BIT_MASK_TABLE0[] = {0x7FU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0xFEU};
const unsigned char BIT_MASK_TABLE1[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
const unsigned char BIT_MASK_TABLE2[] = {0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU};
const unsigned char BIT_MASK_TABLE3[] = {0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U};

#define	WRITE_BIT1(p,i,b)	p[(i)/8] = (b) ? (p[(i)/8] | BIT_MASK_TABLE1[(i)%8]) : (p[(i)/8] & BIT_MASK_TABLE0[(i)%8])
#define	READ_BIT1(p,i)		(p[(i)/8] & BIT_MASK_TABLE1[(i)%8])

#define	WRITE_BIT2(p,i,b)	p[(i)/8] = (b) ? (p[(i)/8] | BIT_MASK_TABLE3[(i)%8]) : (p[(i)/8] & BIT_MASK_TABLE2[(i)%8])
#define	READ_BIT2(p,i)		(p[(i)/8] & BIT_MASK_TABLE3[(i)%8])

const unsigned char INTERLEAVE_TABLE_TX[] = {
  0x00U, 0x04U, 0x04U, 0x00U, 0x07U, 0x04U, 0x0BU, 0x00U, 0x0EU, 0x04U,
  0x12U, 0x00U, 0x15U, 0x04U, 0x19U, 0x00U, 0x1CU, 0x04U, 0x20U, 0x00U,
  0x23U, 0x04U, 0x27U, 0x00U, 0x2AU, 0x04U, 0x2DU, 0x07U, 0x31U, 0x02U,
  0x34U, 0x05U, 0x38U, 0x00U, 0x3BU, 0x03U, 0x3EU, 0x06U, 0x42U, 0x01U,
  0x45U, 0x04U, 0x48U, 0x07U, 0x4CU, 0x02U, 0x4FU, 0x05U, 0x00U, 0x05U,
  0x04U, 0x01U, 0x07U, 0x05U, 0x0BU, 0x01U, 0x0EU, 0x05U, 0x12U, 0x01U,
  0x15U, 0x05U, 0x19U, 0x01U, 0x1CU, 0x05U, 0x20U, 0x01U, 0x23U, 0x05U,
  0x27U, 0x01U, 0x2AU, 0x05U, 0x2EU, 0x00U, 0x31U, 0x03U, 0x34U, 0x06U,
  0x38U, 0x01U, 0x3BU, 0x04U, 0x3EU, 0x07U, 0x42U, 0x02U, 0x45U, 0x05U,
  0x49U, 0x00U, 0x4CU, 0x03U, 0x4FU, 0x06U, 0x00U, 0x06U, 0x04U, 0x02U,
  0x07U, 0x06U, 0x0BU, 0x02U, 0x0EU, 0x06U, 0x12U, 0x02U, 0x15U, 0x06U,
  0x19U, 0x02U, 0x1CU, 0x06U, 0x20U, 0x02U, 0x23U, 0x06U, 0x27U, 0x02U,
  0x2AU, 0x06U, 0x2EU, 0x01U, 0x31U, 0x04U, 0x34U, 0x07U, 0x38U, 0x02U,
  0x3BU, 0x05U, 0x3FU, 0x00U, 0x42U, 0x03U, 0x45U, 0x06U, 0x49U, 0x01U,
  0x4CU, 0x04U, 0x4FU, 0x07U, 0x00U, 0x07U, 0x04U, 0x03U, 0x07U, 0x07U,
  0x0BU, 0x03U, 0x0EU, 0x07U, 0x12U, 0x03U, 0x15U, 0x07U, 0x19U, 0x03U,
  0x1CU, 0x07U, 0x20U, 0x03U, 0x23U, 0x07U, 0x27U, 0x03U, 0x2AU, 0x07U,
  0x2EU, 0x02U, 0x31U, 0x05U, 0x35U, 0x00U, 0x38U, 0x03U, 0x3BU, 0x06U,
  0x3FU, 0x01U, 0x42U, 0x04U, 0x45U, 0x07U, 0x49U, 0x02U, 0x4CU, 0x05U,
  0x50U, 0x00U, 0x01U, 0x00U, 0x04U, 0x04U, 0x08U, 0x00U, 0x0BU, 0x04U,
  0x0FU, 0x00U, 0x12U, 0x04U, 0x16U, 0x00U, 0x19U, 0x04U, 0x1DU, 0x00U,
  0x20U, 0x04U, 0x24U, 0x00U, 0x27U, 0x04U, 0x2BU, 0x00U, 0x2EU, 0x03U,
  0x31U, 0x06U, 0x35U, 0x01U, 0x38U, 0x04U, 0x3BU, 0x07U, 0x3FU, 0x02U,
  0x42U, 0x05U, 0x46U, 0x00U, 0x49U, 0x03U, 0x4CU, 0x06U, 0x50U, 0x01U,
  0x01U, 0x01U, 0x04U, 0x05U, 0x08U, 0x01U, 0x0BU, 0x05U, 0x0FU, 0x01U,
  0x12U, 0x05U, 0x16U, 0x01U, 0x19U, 0x05U, 0x1DU, 0x01U, 0x20U, 0x05U,
  0x24U, 0x01U, 0x27U, 0x05U, 0x2BU, 0x01U, 0x2EU, 0x04U, 0x31U, 0x07U,
  0x35U, 0x02U, 0x38U, 0x05U, 0x3CU, 0x00U, 0x3FU, 0x03U, 0x42U, 0x06U,
  0x46U, 0x01U, 0x49U, 0x04U, 0x4CU, 0x07U, 0x50U, 0x02U, 0x01U, 0x02U,
  0x04U, 0x06U, 0x08U, 0x02U, 0x0BU, 0x06U, 0x0FU, 0x02U, 0x12U, 0x06U,
  0x16U, 0x02U, 0x19U, 0x06U, 0x1DU, 0x02U, 0x20U, 0x06U, 0x24U, 0x02U,
  0x27U, 0x06U, 0x2BU, 0x02U, 0x2EU, 0x05U, 0x32U, 0x00U, 0x35U, 0x03U,
  0x38U, 0x06U, 0x3CU, 0x01U, 0x3FU, 0x04U, 0x42U, 0x07U, 0x46U, 0x02U,
  0x49U, 0x05U, 0x4DU, 0x00U, 0x50U, 0x03U, 0x01U, 0x03U, 0x04U, 0x07U,
  0x08U, 0x03U, 0x0BU, 0x07U, 0x0FU, 0x03U, 0x12U, 0x07U, 0x16U, 0x03U,
  0x19U, 0x07U, 0x1DU, 0x03U, 0x20U, 0x07U, 0x24U, 0x03U, 0x27U, 0x07U,
  0x2BU, 0x03U, 0x2EU, 0x06U, 0x32U, 0x01U, 0x35U, 0x04U, 0x38U, 0x07U,
  0x3CU, 0x02U, 0x3FU, 0x05U, 0x43U, 0x00U, 0x46U, 0x03U, 0x49U, 0x06U,
  0x4DU, 0x01U, 0x50U, 0x04U, 0x01U, 0x04U, 0x05U, 0x00U, 0x08U, 0x04U,
  0x0CU, 0x00U, 0x0FU, 0x04U, 0x13U, 0x00U, 0x16U, 0x04U, 0x1AU, 0x00U,
  0x1DU, 0x04U, 0x21U, 0x00U, 0x24U, 0x04U, 0x28U, 0x00U, 0x2BU, 0x04U,
  0x2EU, 0x07U, 0x32U, 0x02U, 0x35U, 0x05U, 0x39U, 0x00U, 0x3CU, 0x03U,
  0x3FU, 0x06U, 0x43U, 0x01U, 0x46U, 0x04U, 0x49U, 0x07U, 0x4DU, 0x02U,
  0x50U, 0x05U, 0x01U, 0x05U, 0x05U, 0x01U, 0x08U, 0x05U, 0x0CU, 0x01U,
  0x0FU, 0x05U, 0x13U, 0x01U, 0x16U, 0x05U, 0x1AU, 0x01U, 0x1DU, 0x05U,
  0x21U, 0x01U, 0x24U, 0x05U, 0x28U, 0x01U, 0x2BU, 0x05U, 0x2FU, 0x00U,
  0x32U, 0x03U, 0x35U, 0x06U, 0x39U, 0x01U, 0x3CU, 0x04U, 0x3FU, 0x07U,
  0x43U, 0x02U, 0x46U, 0x05U, 0x4AU, 0x00U, 0x4DU, 0x03U, 0x50U, 0x06U,
  0x01U, 0x06U, 0x05U, 0x02U, 0x08U, 0x06U, 0x0CU, 0x02U, 0x0FU, 0x06U,
  0x13U, 0x02U, 0x16U, 0x06U, 0x1AU, 0x02U, 0x1DU, 0x06U, 0x21U, 0x02U,
  0x24U, 0x06U, 0x28U, 0x02U, 0x2BU, 0x06U, 0x2FU, 0x01U, 0x32U, 0x04U,
  0x35U, 0x07U, 0x39U, 0x02U, 0x3CU, 0x05U, 0x40U, 0x00U, 0x43U, 0x03U,
  0x46U, 0x06U, 0x4AU, 0x01U, 0x4DU, 0x04U, 0x50U, 0x07U, 0x01U, 0x07U,
  0x05U, 0x03U, 0x08U, 0x07U, 0x0CU, 0x03U, 0x0FU, 0x07U, 0x13U, 0x03U,
  0x16U, 0x07U, 0x1AU, 0x03U, 0x1DU, 0x07U, 0x21U, 0x03U, 0x24U, 0x07U,
  0x28U, 0x03U, 0x2BU, 0x07U, 0x2FU, 0x02U, 0x32U, 0x05U, 0x36U, 0x00U,
  0x39U, 0x03U, 0x3CU, 0x06U, 0x40U, 0x01U, 0x43U, 0x04U, 0x46U, 0x07U,
  0x4AU, 0x02U, 0x4DU, 0x05U, 0x51U, 0x00U, 0x02U, 0x00U, 0x05U, 0x04U,
  0x09U, 0x00U, 0x0CU, 0x04U, 0x10U, 0x00U, 0x13U, 0x04U, 0x17U, 0x00U,
  0x1AU, 0x04U, 0x1EU, 0x00U, 0x21U, 0x04U, 0x25U, 0x00U, 0x28U, 0x04U,
  0x2CU, 0x00U, 0x2FU, 0x03U, 0x32U, 0x06U, 0x36U, 0x01U, 0x39U, 0x04U,
  0x3CU, 0x07U, 0x40U, 0x02U, 0x43U, 0x05U, 0x47U, 0x00U, 0x4AU, 0x03U,
  0x4DU, 0x06U, 0x51U, 0x01U, 0x02U, 0x01U, 0x05U, 0x05U, 0x09U, 0x01U,
  0x0CU, 0x05U, 0x10U, 0x01U, 0x13U, 0x05U, 0x17U, 0x01U, 0x1AU, 0x05U,
  0x1EU, 0x01U, 0x21U, 0x05U, 0x25U, 0x01U, 0x28U, 0x05U, 0x2CU, 0x01U,
  0x2FU, 0x04U, 0x32U, 0x07U, 0x36U, 0x02U, 0x39U, 0x05U, 0x3DU, 0x00U,
  0x40U, 0x03U, 0x43U, 0x06U, 0x47U, 0x01U, 0x4AU, 0x04U, 0x4DU, 0x07U,
  0x51U, 0x02U, 0x02U, 0x02U, 0x05U, 0x06U, 0x09U, 0x02U, 0x0CU, 0x06U,
  0x10U, 0x02U, 0x13U, 0x06U, 0x17U, 0x02U, 0x1AU, 0x06U, 0x1EU, 0x02U,
  0x21U, 0x06U, 0x25U, 0x02U, 0x28U, 0x06U, 0x2CU, 0x02U, 0x2FU, 0x05U,
  0x33U, 0x00U, 0x36U, 0x03U, 0x39U, 0x06U, 0x3DU, 0x01U, 0x40U, 0x04U,
  0x43U, 0x07U, 0x47U, 0x02U, 0x4AU, 0x05U, 0x4EU, 0x00U, 0x51U, 0x03U,
  0x02U, 0x03U, 0x05U, 0x07U, 0x09U, 0x03U, 0x0CU, 0x07U, 0x10U, 0x03U,
  0x13U, 0x07U, 0x17U, 0x03U, 0x1AU, 0x07U, 0x1EU, 0x03U, 0x21U, 0x07U,
  0x25U, 0x03U, 0x28U, 0x07U, 0x2CU, 0x03U, 0x2FU, 0x06U, 0x33U, 0x01U,
  0x36U, 0x04U, 0x39U, 0x07U, 0x3DU, 0x02U, 0x40U, 0x05U, 0x44U, 0x00U,
  0x47U, 0x03U, 0x4AU, 0x06U, 0x4EU, 0x01U, 0x51U, 0x04U, 0x02U, 0x04U,
  0x06U, 0x00U, 0x09U, 0x04U, 0x0DU, 0x00U, 0x10U, 0x04U, 0x14U, 0x00U,
  0x17U, 0x04U, 0x1BU, 0x00U, 0x1EU, 0x04U, 0x22U, 0x00U, 0x25U, 0x04U,
  0x29U, 0x00U, 0x2CU, 0x04U, 0x2FU, 0x07U, 0x33U, 0x02U, 0x36U, 0x05U,
  0x3AU, 0x00U, 0x3DU, 0x03U, 0x40U, 0x06U, 0x44U, 0x01U, 0x47U, 0x04U,
  0x4AU, 0x07U, 0x4EU, 0x02U, 0x51U, 0x05U, 0x02U, 0x05U, 0x06U, 0x01U,
  0x09U, 0x05U, 0x0DU, 0x01U, 0x10U, 0x05U, 0x14U, 0x01U, 0x17U, 0x05U,
  0x1BU, 0x01U, 0x1EU, 0x05U, 0x22U, 0x01U, 0x25U, 0x05U, 0x29U, 0x01U,
  0x2CU, 0x05U, 0x30U, 0x00U, 0x33U, 0x03U, 0x36U, 0x06U, 0x3AU, 0x01U, 
  0x3DU, 0x04U, 0x40U, 0x07U, 0x44U, 0x02U, 0x47U, 0x05U, 0x4BU, 0x00U,
  0x4EU, 0x03U, 0x51U, 0x06U, 0x02U, 0x06U, 0x06U, 0x02U, 0x09U, 0x06U,
  0x0DU, 0x02U, 0x10U, 0x06U, 0x14U, 0x02U, 0x17U, 0x06U, 0x1BU, 0x02U,
  0x1EU, 0x06U, 0x22U, 0x02U, 0x25U, 0x06U, 0x29U, 0x02U, 0x2CU, 0x06U,
  0x30U, 0x01U, 0x33U, 0x04U, 0x36U, 0x07U, 0x3AU, 0x02U, 0x3DU, 0x05U,
  0x41U, 0x00U, 0x44U, 0x03U, 0x47U, 0x06U, 0x4BU, 0x01U, 0x4EU, 0x04U,
  0x51U, 0x07U, 0x02U, 0x07U, 0x06U, 0x03U, 0x09U, 0x07U, 0x0DU, 0x03U,
  0x10U, 0x07U, 0x14U, 0x03U, 0x17U, 0x07U, 0x1BU, 0x03U, 0x1EU, 0x07U,
  0x22U, 0x03U, 0x25U, 0x07U, 0x29U, 0x03U, 0x2CU, 0x07U, 0x30U, 0x02U,
  0x33U, 0x05U, 0x37U, 0x00U, 0x3AU, 0x03U, 0x3DU, 0x06U, 0x41U, 0x01U,
  0x44U, 0x04U, 0x47U, 0x07U, 0x4BU, 0x02U, 0x4EU, 0x05U, 0x52U, 0x00U,
  0x03U, 0x00U, 0x06U, 0x04U, 0x0AU, 0x00U, 0x0DU, 0x04U, 0x11U, 0x00U,
  0x14U, 0x04U, 0x18U, 0x00U, 0x1BU, 0x04U, 0x1FU, 0x00U, 0x22U, 0x04U,
  0x26U, 0x00U, 0x29U, 0x04U, 0x2DU, 0x00U, 0x30U, 0x03U, 0x33U, 0x06U,
  0x37U, 0x01U, 0x3AU, 0x04U, 0x3DU, 0x07U, 0x41U, 0x02U, 0x44U, 0x05U,
  0x48U, 0x00U, 0x4BU, 0x03U, 0x4EU, 0x06U, 0x52U, 0x01U, 0x03U, 0x01U,
  0x06U, 0x05U, 0x0AU, 0x01U, 0x0DU, 0x05U, 0x11U, 0x01U, 0x14U, 0x05U,
  0x18U, 0x01U, 0x1BU, 0x05U, 0x1FU, 0x01U, 0x22U, 0x05U, 0x26U, 0x01U,
  0x29U, 0x05U, 0x2DU, 0x01U, 0x30U, 0x04U, 0x33U, 0x07U, 0x37U, 0x02U,
  0x3AU, 0x05U, 0x3EU, 0x00U, 0x41U, 0x03U, 0x44U, 0x06U, 0x48U, 0x01U,
  0x4BU, 0x04U, 0x4EU, 0x07U, 0x52U, 0x02U, 0x03U, 0x02U, 0x06U, 0x06U,
  0x0AU, 0x02U, 0x0DU, 0x06U, 0x11U, 0x02U, 0x14U, 0x06U, 0x18U, 0x02U,
  0x1BU, 0x06U, 0x1FU, 0x02U, 0x22U, 0x06U, 0x26U, 0x02U, 0x29U, 0x06U,
  0x2DU, 0x02U, 0x30U, 0x05U, 0x34U, 0x00U, 0x37U, 0x03U, 0x3AU, 0x06U,
  0x3EU, 0x01U, 0x41U, 0x04U, 0x44U, 0x07U, 0x48U, 0x02U, 0x4BU, 0x05U,
  0x4FU, 0x00U, 0x52U, 0x03U, 0x03U, 0x03U, 0x06U, 0x07U, 0x0AU, 0x03U,
  0x0DU, 0x07U, 0x11U, 0x03U, 0x14U, 0x07U, 0x18U, 0x03U, 0x1BU, 0x07U,
  0x1FU, 0x03U, 0x22U, 0x07U, 0x26U, 0x03U, 0x29U, 0x07U, 0x2DU, 0x03U,
  0x30U, 0x06U, 0x34U, 0x01U, 0x37U, 0x04U, 0x3AU, 0x07U, 0x3EU, 0x02U,
  0x41U, 0x05U, 0x45U, 0x00U, 0x48U, 0x03U, 0x4BU, 0x06U, 0x4FU, 0x01U,
  0x52U, 0x04U, 0x03U, 0x04U, 0x07U, 0x00U, 0x0AU, 0x04U, 0x0EU, 0x00U,
  0x11U, 0x04U, 0x15U, 0x00U, 0x18U, 0x04U, 0x1CU, 0x00U, 0x1FU, 0x04U,
  0x23U, 0x00U, 0x26U, 0x04U, 0x2AU, 0x00U, 0x2DU, 0x04U, 0x30U, 0x07U,
  0x34U, 0x02U, 0x37U, 0x05U, 0x3BU, 0x00U, 0x3EU, 0x03U, 0x41U, 0x06U,
  0x45U, 0x01U, 0x48U, 0x04U, 0x4BU, 0x07U, 0x4FU, 0x02U, 0x52U, 0x05U,
  0x03U, 0x05U, 0x07U, 0x01U, 0x0AU, 0x05U, 0x0EU, 0x01U, 0x11U, 0x05U,
  0x15U, 0x01U, 0x18U, 0x05U, 0x1CU, 0x01U, 0x1FU, 0x05U, 0x23U, 0x01U,
  0x26U, 0x05U, 0x2AU, 0x01U, 0x2DU, 0x05U, 0x31U, 0x00U, 0x34U, 0x03U,
  0x37U, 0x06U, 0x3BU, 0x01U, 0x3EU, 0x04U, 0x41U, 0x07U, 0x45U, 0x02U,
  0x48U, 0x05U, 0x4CU, 0x00U, 0x4FU, 0x03U, 0x52U, 0x06U, 0x03U, 0x06U,
  0x07U, 0x02U, 0x0AU, 0x06U, 0x0EU, 0x02U, 0x11U, 0x06U, 0x15U, 0x02U,
  0x18U, 0x06U, 0x1CU, 0x02U, 0x1FU, 0x06U, 0x23U, 0x02U, 0x26U, 0x06U,
  0x2AU, 0x02U, 0x2DU, 0x06U, 0x31U, 0x01U, 0x34U, 0x04U, 0x37U, 0x07U,
  0x3BU, 0x02U, 0x3EU, 0x05U, 0x42U, 0x00U, 0x45U, 0x03U, 0x48U, 0x06U,
  0x4CU, 0x01U, 0x4FU, 0x04U, 0x52U, 0x07U, 0x03U, 0x07U, 0x07U, 0x03U,
  0x0AU, 0x07U, 0x0EU, 0x03U, 0x11U, 0x07U, 0x15U, 0x03U, 0x18U, 0x07U,
  0x1CU, 0x03U, 0x1FU, 0x07U, 0x23U, 0x03U, 0x26U, 0x07U, 0x2AU, 0x03U
};

const unsigned char INTERLEAVE_TABLE_RX[] = {
  0x00U, 0x00U, 0x03U, 0x00U, 0x06U, 0x00U, 0x09U, 0x00U, 0x0CU, 0x00U,
  0x0FU, 0x00U, 0x12U, 0x00U, 0x15U, 0x00U, 0x18U, 0x00U, 0x1BU, 0x00U,
  0x1EU, 0x00U, 0x21U, 0x00U, 0x24U, 0x00U, 0x27U, 0x00U, 0x2AU, 0x00U,
  0x2DU, 0x00U, 0x30U, 0x00U, 0x33U, 0x00U, 0x36U, 0x00U, 0x39U, 0x00U,
  0x3CU, 0x00U, 0x3FU, 0x00U, 0x42U, 0x00U, 0x45U, 0x00U, 0x48U, 0x00U,
  0x4BU, 0x00U, 0x4EU, 0x00U, 0x51U, 0x00U, 0x00U, 0x01U, 0x03U, 0x01U,
  0x06U, 0x01U, 0x09U, 0x01U, 0x0CU, 0x01U, 0x0FU, 0x01U, 0x12U, 0x01U,
  0x15U, 0x01U, 0x18U, 0x01U, 0x1BU, 0x01U, 0x1EU, 0x01U, 0x21U, 0x01U,
  0x24U, 0x01U, 0x27U, 0x01U, 0x2AU, 0x01U, 0x2DU, 0x01U, 0x30U, 0x01U,
  0x33U, 0x01U, 0x36U, 0x01U, 0x39U, 0x01U, 0x3CU, 0x01U, 0x3FU, 0x01U,
  0x42U, 0x01U, 0x45U, 0x01U, 0x48U, 0x01U, 0x4BU, 0x01U, 0x4EU, 0x01U,
  0x51U, 0x01U, 0x00U, 0x02U, 0x03U, 0x02U, 0x06U, 0x02U, 0x09U, 0x02U,
  0x0CU, 0x02U, 0x0FU, 0x02U, 0x12U, 0x02U, 0x15U, 0x02U, 0x18U, 0x02U,
  0x1BU, 0x02U, 0x1EU, 0x02U, 0x21U, 0x02U, 0x24U, 0x02U, 0x27U, 0x02U,
  0x2AU, 0x02U, 0x2DU, 0x02U, 0x30U, 0x02U, 0x33U, 0x02U, 0x36U, 0x02U,
  0x39U, 0x02U, 0x3CU, 0x02U, 0x3FU, 0x02U, 0x42U, 0x02U, 0x45U, 0x02U,
  0x48U, 0x02U, 0x4BU, 0x02U, 0x4EU, 0x02U, 0x51U, 0x02U, 0x00U, 0x03U,
  0x03U, 0x03U, 0x06U, 0x03U, 0x09U, 0x03U, 0x0CU, 0x03U, 0x0FU, 0x03U,
  0x12U, 0x03U, 0x15U, 0x03U, 0x18U, 0x03U, 0x1BU, 0x03U, 0x1EU, 0x03U,
  0x21U, 0x03U, 0x24U, 0x03U, 0x27U, 0x03U, 0x2AU, 0x03U, 0x2DU, 0x03U,
  0x30U, 0x03U, 0x33U, 0x03U, 0x36U, 0x03U, 0x39U, 0x03U, 0x3CU, 0x03U,
  0x3FU, 0x03U, 0x42U, 0x03U, 0x45U, 0x03U, 0x48U, 0x03U, 0x4BU, 0x03U,
  0x4EU, 0x03U, 0x51U, 0x03U, 0x00U, 0x04U, 0x03U, 0x04U, 0x06U, 0x04U,
  0x09U, 0x04U, 0x0CU, 0x04U, 0x0FU, 0x04U, 0x12U, 0x04U, 0x15U, 0x04U,
  0x18U, 0x04U, 0x1BU, 0x04U, 0x1EU, 0x04U, 0x21U, 0x04U, 0x24U, 0x04U,
  0x27U, 0x04U, 0x2AU, 0x04U, 0x2DU, 0x04U, 0x30U, 0x04U, 0x33U, 0x04U,
  0x36U, 0x04U, 0x39U, 0x04U, 0x3CU, 0x04U, 0x3FU, 0x04U, 0x42U, 0x04U,
  0x45U, 0x04U, 0x48U, 0x04U, 0x4BU, 0x04U, 0x4EU, 0x04U, 0x51U, 0x04U,
  0x00U, 0x05U, 0x03U, 0x05U, 0x06U, 0x05U, 0x09U, 0x05U, 0x0CU, 0x05U,
  0x0FU, 0x05U, 0x12U, 0x05U, 0x15U, 0x05U, 0x18U, 0x05U, 0x1BU, 0x05U,
  0x1EU, 0x05U, 0x21U, 0x05U, 0x24U, 0x05U, 0x27U, 0x05U, 0x2AU, 0x05U,
  0x2DU, 0x05U, 0x30U, 0x05U, 0x33U, 0x05U, 0x36U, 0x05U, 0x39U, 0x05U,
  0x3CU, 0x05U, 0x3FU, 0x05U, 0x42U, 0x05U, 0x45U, 0x05U, 0x48U, 0x05U,
  0x4BU, 0x05U, 0x4EU, 0x05U, 0x51U, 0x05U, 0x00U, 0x06U, 0x03U, 0x06U,
  0x06U, 0x06U, 0x09U, 0x06U, 0x0CU, 0x06U, 0x0FU, 0x06U, 0x12U, 0x06U,
  0x15U, 0x06U, 0x18U, 0x06U, 0x1BU, 0x06U, 0x1EU, 0x06U, 0x21U, 0x06U,
  0x24U, 0x06U, 0x27U, 0x06U, 0x2AU, 0x06U, 0x2DU, 0x06U, 0x30U, 0x06U,
  0x33U, 0x06U, 0x36U, 0x06U, 0x39U, 0x06U, 0x3CU, 0x06U, 0x3FU, 0x06U,
  0x42U, 0x06U, 0x45U, 0x06U, 0x48U, 0x06U, 0x4BU, 0x06U, 0x4EU, 0x06U,
  0x51U, 0x06U, 0x00U, 0x07U, 0x03U, 0x07U, 0x06U, 0x07U, 0x09U, 0x07U,
  0x0CU, 0x07U, 0x0FU, 0x07U, 0x12U, 0x07U, 0x15U, 0x07U, 0x18U, 0x07U,
  0x1BU, 0x07U, 0x1EU, 0x07U, 0x21U, 0x07U, 0x24U, 0x07U, 0x27U, 0x07U,
  0x2AU, 0x07U, 0x2DU, 0x07U, 0x30U, 0x07U, 0x33U, 0x07U, 0x36U, 0x07U,
  0x39U, 0x07U, 0x3CU, 0x07U, 0x3FU, 0x07U, 0x42U, 0x07U, 0x45U, 0x07U,
  0x48U, 0x07U, 0x4BU, 0x07U, 0x4EU, 0x07U, 0x51U, 0x07U, 0x01U, 0x00U,
  0x04U, 0x00U, 0x07U, 0x00U, 0x0AU, 0x00U, 0x0DU, 0x00U, 0x10U, 0x00U,
  0x13U, 0x00U, 0x16U, 0x00U, 0x19U, 0x00U, 0x1CU, 0x00U, 0x1FU, 0x00U,
  0x22U, 0x00U, 0x25U, 0x00U, 0x28U, 0x00U, 0x2BU, 0x00U, 0x2EU, 0x00U,
  0x31U, 0x00U, 0x34U, 0x00U, 0x37U, 0x00U, 0x3AU, 0x00U, 0x3DU, 0x00U,
  0x40U, 0x00U, 0x43U, 0x00U, 0x46U, 0x00U, 0x49U, 0x00U, 0x4CU, 0x00U,
  0x4FU, 0x00U, 0x52U, 0x00U, 0x01U, 0x01U, 0x04U, 0x01U, 0x07U, 0x01U,
  0x0AU, 0x01U, 0x0DU, 0x01U, 0x10U, 0x01U, 0x13U, 0x01U, 0x16U, 0x01U,
  0x19U, 0x01U, 0x1CU, 0x01U, 0x1FU, 0x01U, 0x22U, 0x01U, 0x25U, 0x01U,
  0x28U, 0x01U, 0x2BU, 0x01U, 0x2EU, 0x01U, 0x31U, 0x01U, 0x34U, 0x01U,
  0x37U, 0x01U, 0x3AU, 0x01U, 0x3DU, 0x01U, 0x40U, 0x01U, 0x43U, 0x01U,
  0x46U, 0x01U, 0x49U, 0x01U, 0x4CU, 0x01U, 0x4FU, 0x01U, 0x52U, 0x01U,
  0x01U, 0x02U, 0x04U, 0x02U, 0x07U, 0x02U, 0x0AU, 0x02U, 0x0DU, 0x02U,
  0x10U, 0x02U, 0x13U, 0x02U, 0x16U, 0x02U, 0x19U, 0x02U, 0x1CU, 0x02U,
  0x1FU, 0x02U, 0x22U, 0x02U, 0x25U, 0x02U, 0x28U, 0x02U, 0x2BU, 0x02U,
  0x2EU, 0x02U, 0x31U, 0x02U, 0x34U, 0x02U, 0x37U, 0x02U, 0x3AU, 0x02U,
  0x3DU, 0x02U, 0x40U, 0x02U, 0x43U, 0x02U, 0x46U, 0x02U, 0x49U, 0x02U,
  0x4CU, 0x02U, 0x4FU, 0x02U, 0x52U, 0x02U, 0x01U, 0x03U, 0x04U, 0x03U,
  0x07U, 0x03U, 0x0AU, 0x03U, 0x0DU, 0x03U, 0x10U, 0x03U, 0x13U, 0x03U,
  0x16U, 0x03U, 0x19U, 0x03U, 0x1CU, 0x03U, 0x1FU, 0x03U, 0x22U, 0x03U,
  0x25U, 0x03U, 0x28U, 0x03U, 0x2BU, 0x03U, 0x2EU, 0x03U, 0x31U, 0x03U,
  0x34U, 0x03U, 0x37U, 0x03U, 0x3AU, 0x03U, 0x3DU, 0x03U, 0x40U, 0x03U,
  0x43U, 0x03U, 0x46U, 0x03U, 0x49U, 0x03U, 0x4CU, 0x03U, 0x4FU, 0x03U,
  0x52U, 0x03U, 0x01U, 0x04U, 0x04U, 0x04U, 0x07U, 0x04U, 0x0AU, 0x04U,
  0x0DU, 0x04U, 0x10U, 0x04U, 0x13U, 0x04U, 0x16U, 0x04U, 0x19U, 0x04U,
  0x1CU, 0x04U, 0x1FU, 0x04U, 0x22U, 0x04U, 0x25U, 0x04U, 0x28U, 0x04U,
  0x2BU, 0x04U, 0x2EU, 0x04U, 0x31U, 0x04U, 0x34U, 0x04U, 0x37U, 0x04U,
  0x3AU, 0x04U, 0x3DU, 0x04U, 0x40U, 0x04U, 0x43U, 0x04U, 0x46U, 0x04U,
  0x49U, 0x04U, 0x4CU, 0x04U, 0x4FU, 0x04U, 0x01U, 0x05U, 0x04U, 0x05U,
  0x07U, 0x05U, 0x0AU, 0x05U, 0x0DU, 0x05U, 0x10U, 0x05U, 0x13U, 0x05U,
  0x16U, 0x05U, 0x19U, 0x05U, 0x1CU, 0x05U, 0x1FU, 0x05U, 0x22U, 0x05U,
  0x25U, 0x05U, 0x28U, 0x05U, 0x2BU, 0x05U, 0x2EU, 0x05U, 0x31U, 0x05U,
  0x34U, 0x05U, 0x37U, 0x05U, 0x3AU, 0x05U, 0x3DU, 0x05U, 0x40U, 0x05U,
  0x43U, 0x05U, 0x46U, 0x05U, 0x49U, 0x05U, 0x4CU, 0x05U, 0x4FU, 0x05U,
  0x01U, 0x06U, 0x04U, 0x06U, 0x07U, 0x06U, 0x0AU, 0x06U, 0x0DU, 0x06U,
  0x10U, 0x06U, 0x13U, 0x06U, 0x16U, 0x06U, 0x19U, 0x06U, 0x1CU, 0x06U,
  0x1FU, 0x06U, 0x22U, 0x06U, 0x25U, 0x06U, 0x28U, 0x06U, 0x2BU, 0x06U,
  0x2EU, 0x06U, 0x31U, 0x06U, 0x34U, 0x06U, 0x37U, 0x06U, 0x3AU, 0x06U,
  0x3DU, 0x06U, 0x40U, 0x06U, 0x43U, 0x06U, 0x46U, 0x06U, 0x49U, 0x06U,
  0x4CU, 0x06U, 0x4FU, 0x06U, 0x01U, 0x07U, 0x04U, 0x07U, 0x07U, 0x07U,
  0x0AU, 0x07U, 0x0DU, 0x07U, 0x10U, 0x07U, 0x13U, 0x07U, 0x16U, 0x07U,
  0x19U, 0x07U, 0x1CU, 0x07U, 0x1FU, 0x07U, 0x22U, 0x07U, 0x25U, 0x07U,
  0x28U, 0x07U, 0x2BU, 0x07U, 0x2EU, 0x07U, 0x31U, 0x07U, 0x34U, 0x07U,
  0x37U, 0x07U, 0x3AU, 0x07U, 0x3DU, 0x07U, 0x40U, 0x07U, 0x43U, 0x07U,
  0x46U, 0x07U, 0x49U, 0x07U, 0x4CU, 0x07U, 0x4FU, 0x07U, 0x02U, 0x00U,
  0x05U, 0x00U, 0x08U, 0x00U, 0x0BU, 0x00U, 0x0EU, 0x00U, 0x11U, 0x00U,
  0x14U, 0x00U, 0x17U, 0x00U, 0x1AU, 0x00U, 0x1DU, 0x00U, 0x20U, 0x00U,
  0x23U, 0x00U, 0x26U, 0x00U, 0x29U, 0x00U, 0x2CU, 0x00U, 0x2FU, 0x00U,
  0x32U, 0x00U, 0x35U, 0x00U, 0x38U, 0x00U, 0x3BU, 0x00U, 0x3EU, 0x00U,
  0x41U, 0x00U, 0x44U, 0x00U, 0x47U, 0x00U, 0x4AU, 0x00U, 0x4DU, 0x00U,
  0x50U, 0x00U, 0x02U, 0x01U, 0x05U, 0x01U, 0x08U, 0x01U, 0x0BU, 0x01U,
  0x0EU, 0x01U, 0x11U, 0x01U, 0x14U, 0x01U, 0x17U, 0x01U, 0x1AU, 0x01U,
  0x1DU, 0x01U, 0x20U, 0x01U, 0x23U, 0x01U, 0x26U, 0x01U, 0x29U, 0x01U,
  0x2CU, 0x01U, 0x2FU, 0x01U, 0x32U, 0x01U, 0x35U, 0x01U, 0x38U, 0x01U,
  0x3BU, 0x01U, 0x3EU, 0x01U, 0x41U, 0x01U, 0x44U, 0x01U, 0x47U, 0x01U,
  0x4AU, 0x01U, 0x4DU, 0x01U, 0x50U, 0x01U, 0x02U, 0x02U, 0x05U, 0x02U,
  0x08U, 0x02U, 0x0BU, 0x02U, 0x0EU, 0x02U, 0x11U, 0x02U, 0x14U, 0x02U,
  0x17U, 0x02U, 0x1AU, 0x02U, 0x1DU, 0x02U, 0x20U, 0x02U, 0x23U, 0x02U,
  0x26U, 0x02U, 0x29U, 0x02U, 0x2CU, 0x02U, 0x2FU, 0x02U, 0x32U, 0x02U,
  0x35U, 0x02U, 0x38U, 0x02U, 0x3BU, 0x02U, 0x3EU, 0x02U, 0x41U, 0x02U,
  0x44U, 0x02U, 0x47U, 0x02U, 0x4AU, 0x02U, 0x4DU, 0x02U, 0x50U, 0x02U,
  0x02U, 0x03U, 0x05U, 0x03U, 0x08U, 0x03U, 0x0BU, 0x03U, 0x0EU, 0x03U,
  0x11U, 0x03U, 0x14U, 0x03U, 0x17U, 0x03U, 0x1AU, 0x03U, 0x1DU, 0x03U,
  0x20U, 0x03U, 0x23U, 0x03U, 0x26U, 0x03U, 0x29U, 0x03U, 0x2CU, 0x03U,
  0x2FU, 0x03U, 0x32U, 0x03U, 0x35U, 0x03U, 0x38U, 0x03U, 0x3BU, 0x03U,
  0x3EU, 0x03U, 0x41U, 0x03U, 0x44U, 0x03U, 0x47U, 0x03U, 0x4AU, 0x03U,
  0x4DU, 0x03U, 0x50U, 0x03U, 0x02U, 0x04U, 0x05U, 0x04U, 0x08U, 0x04U,
  0x0BU, 0x04U, 0x0EU, 0x04U, 0x11U, 0x04U, 0x14U, 0x04U, 0x17U, 0x04U,
  0x1AU, 0x04U, 0x1DU, 0x04U, 0x20U, 0x04U, 0x23U, 0x04U, 0x26U, 0x04U,
  0x29U, 0x04U, 0x2CU, 0x04U, 0x2FU, 0x04U, 0x32U, 0x04U, 0x35U, 0x04U,
  0x38U, 0x04U, 0x3BU, 0x04U, 0x3EU, 0x04U, 0x41U, 0x04U, 0x44U, 0x04U,
  0x47U, 0x04U, 0x4AU, 0x04U, 0x4DU, 0x04U, 0x50U, 0x04U, 0x02U, 0x05U,
  0x05U, 0x05U, 0x08U, 0x05U, 0x0BU, 0x05U, 0x0EU, 0x05U, 0x11U, 0x05U,
  0x14U, 0x05U, 0x17U, 0x05U, 0x1AU, 0x05U, 0x1DU, 0x05U, 0x20U, 0x05U,
  0x23U, 0x05U, 0x26U, 0x05U, 0x29U, 0x05U, 0x2CU, 0x05U, 0x2FU, 0x05U,
  0x32U, 0x05U, 0x35U, 0x05U, 0x38U, 0x05U, 0x3BU, 0x05U, 0x3EU, 0x05U,
  0x41U, 0x05U, 0x44U, 0x05U, 0x47U, 0x05U, 0x4AU, 0x05U, 0x4DU, 0x05U,
  0x50U, 0x05U, 0x02U, 0x06U, 0x05U, 0x06U, 0x08U, 0x06U, 0x0BU, 0x06U,
  0x0EU, 0x06U, 0x11U, 0x06U, 0x14U, 0x06U, 0x17U, 0x06U, 0x1AU, 0x06U,
  0x1DU, 0x06U, 0x20U, 0x06U, 0x23U, 0x06U, 0x26U, 0x06U, 0x29U, 0x06U,
  0x2CU, 0x06U, 0x2FU, 0x06U, 0x32U, 0x06U, 0x35U, 0x06U, 0x38U, 0x06U,
  0x3BU, 0x06U, 0x3EU, 0x06U, 0x41U, 0x06U, 0x44U, 0x06U, 0x47U, 0x06U,
  0x4AU, 0x06U, 0x4DU, 0x06U, 0x50U, 0x06U, 0x02U, 0x07U, 0x05U, 0x07U,
  0x08U, 0x07U, 0x0BU, 0x07U, 0x0EU, 0x07U, 0x11U, 0x07U, 0x14U, 0x07U,
  0x17U, 0x07U, 0x1AU, 0x07U, 0x1DU, 0x07U, 0x20U, 0x07U, 0x23U, 0x07U,
  0x26U, 0x07U, 0x29U, 0x07U, 0x2CU, 0x07U, 0x2FU, 0x07U, 0x32U, 0x07U,
  0x35U, 0x07U, 0x38U, 0x07U, 0x3BU, 0x07U, 0x3EU, 0x07U, 0x41U, 0x07U,
  0x44U, 0x07U, 0x47U, 0x07U, 0x4AU, 0x07U, 0x4DU, 0x07U, 0x50U, 0x07U,
};

const unsigned char SCRAMBLE_TABLE_TX[] = {
  0x00U, 0xF7U, 0x34U, 0x09U, 0x44U, 0x46U, 0xD7U, 0x06U, 0xB3U, 0x72U,
  0xDEU, 0x42U, 0xF5U, 0xA5U, 0xD8U, 0xF1U, 0x87U, 0x7BU, 0x9AU, 0x04U,
  0x22U, 0xA3U, 0x6BU, 0x83U, 0x59U, 0x39U, 0x6FU, 0xA1U, 0xFAU, 0x52U,
  0xECU, 0xF8U, 0xC3U, 0x3DU, 0x4DU, 0x02U, 0x91U, 0xD1U, 0xB5U, 0xC1U,
  0xACU, 0x9CU, 0xB7U, 0x50U, 0x7DU, 0x29U, 0x76U, 0xFCU, 0xE1U, 0x9EU,
  0x26U, 0x81U, 0xC8U, 0xE8U, 0xDAU, 0x60U, 0x56U, 0xCEU, 0x5BU, 0xA8U,
  0xBEU, 0x14U, 0x3BU, 0xFEU, 0x70U, 0x4FU, 0x93U, 0x40U, 0x64U, 0x74U,
  0x6DU, 0x30U, 0x2BU, 0xE7U, 0x2DU, 0x54U, 0x5FU, 0x8AU, 0x1DU, 0x7FU,
  0xB8U, 0xA7U, 0x49U, 0x20U, 0x32U, 0xBAU, 0x36U, 0x98U, 0x95U, 0xF3U,
  0x06U};

const unsigned char SCRAMBLE_TABLE_RX[] = {
  0x70U, 0x4FU, 0x93U, 0x40U, 0x64U, 0x74U, 0x6DU, 0x30U, 0x2BU, 0xE7U,
  0x2DU, 0x54U, 0x5FU, 0x8AU, 0x1DU, 0x7FU, 0xB8U, 0xA7U, 0x49U, 0x20U,
  0x32U, 0xBAU, 0x36U, 0x98U, 0x95U, 0xF3U, 0x16U, 0xAAU, 0x2FU, 0xC5U,
  0x8EU, 0x3FU, 0xDCU, 0xD3U, 0x24U, 0x10U, 0x19U, 0x5DU, 0x1BU, 0xCCU,
  0xCAU, 0x79U, 0x0BU, 0xD5U, 0x97U, 0x62U, 0xC7U, 0x1FU, 0xEEU, 0x69U,
  0x12U, 0x88U, 0x8CU, 0xAEU, 0x0DU, 0x66U, 0xE5U, 0xBCU, 0x85U, 0xEAU,
  0x4BU, 0xB1U, 0xE3U, 0x0FU, 0xF7U, 0x34U, 0x09U, 0x44U, 0x46U, 0xD7U,
  0x06U, 0xB3U, 0x72U, 0xDEU, 0x42U, 0xF5U, 0xA5U, 0xD8U, 0xF1U, 0x87U,
  0x7BU, 0x9AU, 0x04U, 0x22U, 0xA3U, 0x6BU, 0x83U, 0x59U, 0x39U, 0x6FU,
  0x00U};

CSoundCardController::CSoundCardController(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) :
CModem(),
m_sound(rxDevice, txDevice, DSTAR_RADIO_SAMPLE_RATE, DSTAR_RADIO_BLOCK_SIZE),
m_rxLevel(rxLevel),
m_txLevel(txLevel),
m_txDelay(txDelay),
m_txTail(txTail),
m_txAudio(48000U),
m_rxAudio(4800U),
m_rxState(DSRSCCS_NONE),
m_patternBuffer(0x00U),
m_demodulator(),
m_modulator(),
m_rxBuffer(NULL),
m_rxBufferBits(0U),
m_dataBits(0U),
m_mar(0U),
m_pathMetric(NULL),
m_pathMemory0(NULL),
m_pathMemory1(NULL),
m_pathMemory2(NULL),
m_pathMemory3(NULL),
m_fecOutput(NULL)
{
	wxASSERT(!rxDevice.IsEmpty());
	wxASSERT(!txDevice.IsEmpty());

	m_modulator.setInvert(txInvert);
	m_demodulator.setInvert(rxInvert);

	m_sound.setCallback(this, 0U);

	m_rxBuffer   = new unsigned char[FEC_SECTION_LENGTH_BYTES];

	m_pathMetric  = new int[4U];
	m_pathMemory0 = new unsigned int[42U];
	m_pathMemory1 = new unsigned int[42U];
	m_pathMemory2 = new unsigned int[42U];
	m_pathMemory3 = new unsigned int[42U];
	m_fecOutput   = new unsigned char[42U];
}

CSoundCardController::~CSoundCardController()
{
	delete[] m_rxBuffer;
	delete[] m_pathMetric;
	delete[] m_pathMemory0;
	delete[] m_pathMemory1;
	delete[] m_pathMemory2;
	delete[] m_pathMemory3;
	delete[] m_fecOutput;
}

bool CSoundCardController::start()
{
	bool ret = m_sound.open();
	if (!ret)
		return false;

	Create();
	SetPriority(100U);
	Run();

	return true;
}

void* CSoundCardController::Entry()
{
	wxLogMessage(wxT("Starting Sound Card Controller thread"));

	while (!m_stopped) {
		wxFloat32 val;
		while (m_rxAudio.getData(&val, 1U) == 1U) {
			TRISTATE state = m_demodulator.decode(val * m_rxLevel);
			switch (state) {
				case STATE_TRUE:
					switch (m_rxState) {
						case DSRSCCS_NONE:
							processNone(true);
							break;
						case DSRSCCS_HEADER:
							processHeader(true);
							break;
						case DSRSCCS_DATA:
							processData(true);
							break;
						default:
							break;
					}
					break;
				case STATE_FALSE:
					switch (m_rxState) {
						case DSRSCCS_NONE:
							processNone(false);
							break;
						case DSRSCCS_HEADER:
							processHeader(false);
							break;
						case DSRSCCS_DATA:
							processData(false);
							break;
						default:
							break;
					}
					break;
				default:
					break;
			}
		}

		Sleep(10UL);
	}

	wxLogMessage(wxT("Stopping Sound Card Controller thread"));

	m_sound.close();

	return NULL;
}

bool CSoundCardController::writeHeader(const CHeaderData& header)
{
	bool ret = m_txAudio.hasSpace((m_txDelay + 60U + 85U) * 8U * DSTAR_RADIO_BIT_LENGTH);
	if (!ret) {
		wxLogWarning(wxT("No space to write the header"));
		return false;
	}

	unsigned char buffer1[RADIO_HEADER_LENGTH_BYTES];

	::memset(buffer1, ' ', RADIO_HEADER_LENGTH_BYTES);

	buffer1[0U] = header.getFlag1();
	buffer1[1U] = header.getFlag2();
	buffer1[2U] = header.getFlag3();

	wxString rpt2 = header.getRptCall2();
	for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++)
		buffer1[i + 3U]  = rpt2.GetChar(i);

	wxString rpt1 = header.getRptCall1();
	for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
		buffer1[i + 11U] = rpt1.GetChar(i);

	wxString your = header.getYourCall();
	for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++)
		buffer1[i + 19U] = your.GetChar(i);

	wxString my1 = header.getMyCall1();
	for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
		buffer1[i + 27U] = my1.GetChar(i);

	wxString my2 = header.getMyCall2();
	for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++)
		buffer1[i + 35U] = my2.GetChar(i);

	CCCITTChecksumReverse cksum1;
	cksum1.update(buffer1 + 0U, RADIO_HEADER_LENGTH_BYTES - 2U);
	cksum1.result(buffer1 + 39U);

	unsigned int bytes = m_txDelay + 60U;
	for (unsigned int i = 0U; i < bytes; i++)
		writeBits(BIT_SYNC);

	unsigned char buffer2[86U];
	txHeader(buffer1, buffer2 + 2U);

	buffer2[0U]  = FRAME_SYNC0;
	buffer2[1U]  = FRAME_SYNC1;
	buffer2[2U] |= FRAME_SYNC2;

	for (unsigned int i = 0U; i < 85U; i++)
		writeBits(buffer2[i]);

	return true;
}

bool CSoundCardController::writeData(const unsigned char* data, unsigned int length, bool end)
{
	if (end) {
		unsigned int tailBlocks = 3U + m_txTail / 10U;

		bool ret = m_txAudio.hasSpace(tailBlocks * END_PATTERN_LENGTH_BYTES * 8U * DSTAR_RADIO_BIT_LENGTH);
		if (!ret) {
			wxLogWarning(wxT("No space to write end data"));
			return false;
		}

		for (unsigned int j = 0U; j < tailBlocks; j++) {
			for (unsigned int i = 0U; i < END_PATTERN_LENGTH_BYTES; i++)
				writeBits(END_PATTERN_BYTES[i]);
		}
	} else {
		bool ret = m_txAudio.hasSpace(length * 8U * DSTAR_RADIO_BIT_LENGTH);
		if (!ret) {
			wxLogWarning(wxT("No space to write data"));
			return false;
		}

		for (unsigned int i = 0U; i < length; i++)
			writeBits(data[i]);
	}

	return true;
}

unsigned int CSoundCardController::getSpace()
{
	return m_txAudio.freeSpace() / (DV_FRAME_LENGTH_BYTES * 8U * DSTAR_RADIO_BIT_LENGTH);
}

bool CSoundCardController::isTXReady()
{
	return m_txAudio.isEmpty();
}

bool CSoundCardController::isTX()
{
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__)
	return m_txAudio.hasData();
#else
        return m_sound.isWriterBusy() || m_txAudio.hasData();
#endif
}

void CSoundCardController::readCallback(const wxFloat32* input, unsigned int n, int id)
{
#if !defined(AUDIO_LOOPBACK)
	if (!m_stopped)
		m_rxAudio.addData(input, n);
#endif
}

void CSoundCardController::writeCallback(wxFloat32* output, unsigned int& n, int id)
{
        if (n == 0U)
                return;

	::memset(output, 0x00, n * sizeof(wxFloat32));

	if (!m_stopped) {
		n = m_txAudio.getData(output, n);
#if defined(AUDIO_LOOPBACK)
		n = m_rxAudio.addData(output, n);
#endif
	}
}

void CSoundCardController::txHeader(const unsigned char* in, unsigned char* out)
{
	unsigned char intermediate[84U];
	unsigned int i;

	for (i = 0U; i < 83U; i++) {
		intermediate[i] = 0x00U;
		out[i] = 0x00U;
	}

	// Convolve the header
	unsigned char d, d1 = 0U, d2 = 0U, g0, g1;
	unsigned int k = 0U;
	for (i = 0U; i < 42U; i++) {
		for (unsigned int j = 0U; j < 8U; j++) {
			unsigned char mask = (0x01U << j);
			d = 0U;

			if (in[i] & mask)
				d = 1U;

			g0 = (d + d2) % 2U;
			g1 = (d + d1 + d2) % 2U;
			d2 = d1;
			d1 = d;

			if (g1)
				intermediate[k / 8U] |= BIT_MASK_TABLE1[k % 8U];

			k++;

			if (g0)
				intermediate[k / 8U] |= BIT_MASK_TABLE1[k % 8U];

			k++;
		}
	}

	// Interleave the header
	i = 0U;
	while (i < 660U) {
		unsigned char d = intermediate[i / 8U];

		if (d & 0x80U)
			out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
		i++;

		if (d & 0x40U)
			out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
		i++;

		if (d & 0x20U)
			out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
		i++;

		if (d & 0x10U)
			out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
		i++;

		if (i < 660U) {
			if (d & 0x08U)
				out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
			i++;

			if (d & 0x04U)
				out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
			i++;

			if (d & 0x02U)
				out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
			i++;

			if (d & 0x01U)
				out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
			i++;
		}
	}

	// Scramble the header
	for (i = 0U; i < 83U; i++)
		out[i] ^= SCRAMBLE_TABLE_TX[i];
}

void CSoundCardController::writeBits(unsigned char c)
{
	wxFloat32 buffer[DSTAR_RADIO_BIT_LENGTH];

	unsigned char mask = 0x01U;
	for (unsigned int i = 0U; i < 8U; i++) {
		bool bit = (c & mask) == mask;

		m_modulator.code(bit, buffer, DSTAR_RADIO_BIT_LENGTH);

		for (unsigned int j = 0U; j < DSTAR_RADIO_BIT_LENGTH; j++)
			buffer[j] *= m_txLevel;

		m_txAudio.addData(buffer, DSTAR_RADIO_BIT_LENGTH);

		mask <<= 1;
	}
}

void CSoundCardController::processNone(bool bit)
{
	m_patternBuffer <<= 1;
    if (bit)
		m_patternBuffer |= 0x01U;

	// Exact matching of the frame sync sequence
	unsigned int errs = countBits((m_patternBuffer & FRAME_SYNC_MASK) ^ FRAME_SYNC_DATA);
	if (errs == 0U) {
		// Lock the GMSK PLL to this signal
		m_demodulator.lock(true);

		::memset(m_rxBuffer, 0x00U, FEC_SECTION_LENGTH_BYTES);
		m_rxBufferBits = 0U;
		m_rxState = DSRSCCS_HEADER;
		return;
	}

	// Exact matching of the data sync bit sequence
	errs = countBits((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA);
	if (errs == 0U) {
		// Lock the GMSK PLL to this signal
		m_demodulator.lock(true);

		wxMutexLocker locker(m_mutex);

		unsigned char data[2U];
		data[0U] = DSMTT_DATA;
		data[1U] = DV_FRAME_LENGTH_BYTES;
		m_rxData.addData(data, 2U);

		m_rxData.addData(NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
		m_rxData.addData(DATA_SYNC_BYTES,      DATA_FRAME_LENGTH_BYTES);

		::memset(m_rxBuffer, 0x00U, DV_FRAME_LENGTH_BYTES);
		m_rxBufferBits = 0U;

		m_dataBits = MAX_SYNC_BITS;
		m_rxState = DSRSCCS_DATA;
		return;
	}
}

void CSoundCardController::processHeader(bool bit)
{
	m_patternBuffer <<= 1;
	if (bit)
		m_patternBuffer |= 0x01U;

	WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit);
	m_rxBufferBits++;

	// A full FEC header
	if (m_rxBufferBits == FEC_SECTION_LENGTH_BITS) {
		// Process the scrambling, interleaving and FEC, then return if the chcksum was correct
		unsigned char header[RADIO_HEADER_LENGTH_BYTES];
		bool ok = rxHeader(m_rxBuffer, header);
		if (ok) {
			// The checksum is correct
			wxMutexLocker locker(m_mutex);

			unsigned char data[2U];
			data[0U] = DSMTT_HEADER;
			data[1U] = RADIO_HEADER_LENGTH_BYTES;
			m_rxData.addData(data, 2U);

			m_rxData.addData(header, RADIO_HEADER_LENGTH_BYTES);

			::memset(m_rxBuffer, 0x00U, DV_FRAME_LENGTH_BYTES);
			m_rxBufferBits = 0U;

			m_rxState = DSRSCCS_DATA;
			m_dataBits = MAX_SYNC_BITS;
		} else {
			// Release PLL ACQ
			m_demodulator.lock(false);

			// The checksum failed, return to looking for syncs
			m_rxState = DSRSCCS_NONE;
		}
	}
}

void CSoundCardController::processData(bool bit)
{
	m_patternBuffer <<= 1;
	if (bit)
		m_patternBuffer |= 0x01U;

	WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit);
	m_rxBufferBits++;

	// Fuzzy matching of the end frame sequences
	unsigned int errs = countBits((m_patternBuffer & END_SYNC_MASK) ^ END_SYNC_DATA);
	if (errs <= END_SYNC_ERRS) {
		// Release the GMSK PLL
		m_demodulator.lock(false);

		wxMutexLocker locker(m_mutex);

		unsigned char data[2U];
		data[0U] = DSMTT_EOT;
		data[1U] = 0U;
		m_rxData.addData(data, 2U);

		m_rxState = DSRSCCS_NONE;
		return;
	}

	// Fuzzy matching of the data sync bit sequence
	bool syncSeen = false;
	if (m_rxBufferBits >= (DV_FRAME_LENGTH_BITS - 3U)) {
		errs = countBits((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA);
		if (errs <= DATA_SYNC_ERRS) {
			m_rxBufferBits = DV_FRAME_LENGTH_BITS;
			m_dataBits = MAX_SYNC_BITS;
			syncSeen = true;
		}
	}

	// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
	m_dataBits--;
	if (m_dataBits == 0U) {
		// Release the GMSK PLL
		m_demodulator.lock(false);

		wxMutexLocker locker(m_mutex);

		unsigned char data[2U];
		data[0U] = DSMTT_LOST;
		data[1U] = 0U;
		m_rxData.addData(data, 2U);

		m_rxState = DSRSCCS_NONE;
		return;
	}

	// Check to see if the sync is arriving late
	if (m_rxBufferBits == DV_FRAME_LENGTH_BITS && !syncSeen) {
		for (unsigned int i = 1U; i <= 3U; i++) {
			wxUint32 syncMask = DATA_SYNC_MASK >> i;
			wxUint32 syncData = DATA_SYNC_DATA >> i;
			errs = countBits((m_patternBuffer & syncMask) ^ syncData);
			if (errs <= DATA_SYNC_ERRS) {
				m_rxBufferBits -= i;
				break;
			}
		}
	}

	// Send a data frame to the host if the required number of bits have been received, or if a data sync has been seen
	if (m_rxBufferBits == DV_FRAME_LENGTH_BITS) {
		if (syncSeen) {
			m_rxBuffer[9U]  = DATA_SYNC_BYTES[0U];
			m_rxBuffer[10U] = DATA_SYNC_BYTES[1U];
			m_rxBuffer[11U] = DATA_SYNC_BYTES[2U];
		}

		wxMutexLocker locker(m_mutex);

		unsigned char data[2U];
		data[0U] = DSMTT_DATA;
		data[1U] = DV_FRAME_LENGTH_BYTES;
		m_rxData.addData(data, 2U);

		m_rxData.addData(m_rxBuffer, DV_FRAME_LENGTH_BYTES);

		// Start the next frame
		::memset(m_rxBuffer, 0x00U, DV_FRAME_LENGTH_BYTES);
		m_rxBufferBits = 0U;
	}
}

unsigned int CSoundCardController::countBits(wxUint32 num)
{
    unsigned int count = 0U;

    for (unsigned int i = 0U; i < 8U; i++)
        count += NIBBLE_BITS[(num >> (i * 4U)) & 0x0FU];

    return count;
}

bool CSoundCardController::rxHeader(unsigned char* in, unsigned char* out)
{
	int i;

	// Descramble the header
	for (i = 0; i < int(FEC_SECTION_LENGTH_BYTES); i++)
		in[i] ^= SCRAMBLE_TABLE_RX[i];

	unsigned char intermediate[84U];
	for (i = 0; i < 84; i++)
		intermediate[i] = 0x00U;

	// Deinterleave the header
	i = 0;
	while (i < 660) {
		unsigned char d = in[i / 8];

		if (d & 0x01U)
			intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
		i++;

		if (d & 0x02U)
			intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
		i++;

		if (d & 0x04U)
			intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
		i++;

		if (d & 0x08U)
			intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
		i++;

		if (i < 660) {
			if (d & 0x10U)
				intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
			i++;

			if (d & 0x20U)
				intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
			i++;

			if (d & 0x40U)
				intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
			i++;

			if (d & 0x80U)
				intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]);
			i++;
		}
	}

	for (i = 0; i < 4; i++)
		m_pathMetric[i] = 0;

	int decodeData[2U];

	m_mar = 0U;
	for (i = 0; i < 660; i += 2) {
		if (intermediate[i / 8] & (0x80U >> i % 8))
			decodeData[1U] = 1U;
		else
			decodeData[1U] = 0U;

		if (intermediate[i / 8] & (0x40U >> i % 8))
			decodeData[0U] = 1U;
		else
			decodeData[0U] = 0U;

		viterbiDecode(decodeData);
	}

	traceBack();

	for (i = 0; i < int(RADIO_HEADER_LENGTH_BYTES); i++)
		out[i] = 0x00U;

	unsigned int j = 0;
	for (i = 329; i >= 0; i--) {
		if (READ_BIT1(m_fecOutput, i))
			out[j / 8] |= (0x01U << (j % 8));

		j++;
	}

	CCCITTChecksumReverse cksum;
	cksum.update(out, RADIO_HEADER_LENGTH_BYTES - 2U);

	return cksum.check(out + RADIO_HEADER_LENGTH_BYTES - 2U);
}

void CSoundCardController::acs(int* metric)
{
	int tempMetric[4U];

	unsigned int j = m_mar / 8U;
	unsigned int k = m_mar % 8U;

	// Pres. state = S0, Prev. state = S0 & S2
	int m1 = metric[0U] + m_pathMetric[0U];
	int m2 = metric[4U] + m_pathMetric[2U];
	tempMetric[0U] = m1 < m2 ? m1 : m2;
	if (m1 < m2)
		m_pathMemory0[j] &= BIT_MASK_TABLE0[k];
	else
		m_pathMemory0[j] |= BIT_MASK_TABLE1[k];

	// Pres. state = S1, Prev. state = S0 & S2
	m1 = metric[1U] + m_pathMetric[0U];
	m2 = metric[5U] + m_pathMetric[2U];
	tempMetric[1U] = m1 < m2 ? m1 : m2;
	if (m1 < m2)
		m_pathMemory1[j] &= BIT_MASK_TABLE0[k];
	else
		m_pathMemory1[j] |= BIT_MASK_TABLE1[k];

	// Pres. state = S2, Prev. state = S2 & S3
	m1 = metric[2U] + m_pathMetric[1U];
	m2 = metric[6U] + m_pathMetric[3U];
	tempMetric[2U] = m1 < m2 ? m1 : m2;
	if (m1 < m2)
		m_pathMemory2[j] &= BIT_MASK_TABLE0[k];
	else
		m_pathMemory2[j] |= BIT_MASK_TABLE1[k];

	// Pres. state = S3, Prev. state = S1 & S3
 	m1 = metric[3U] + m_pathMetric[1U];
	m2 = metric[7U] + m_pathMetric[3U];
	tempMetric[3U] = m1 < m2 ? m1 : m2;
	if (m1 < m2)
		m_pathMemory3[j] &= BIT_MASK_TABLE0[k];
	else
		m_pathMemory3[j] |= BIT_MASK_TABLE1[k];

	for (unsigned int i = 0U; i < 4U; i++)
		m_pathMetric[i] = tempMetric[i];

	m_mar++;
}
 
void CSoundCardController::viterbiDecode(int* data)
{
	int metric[8U];

	metric[0] = (data[1] ^ 0) + (data[0] ^ 0);
	metric[1] = (data[1] ^ 1) + (data[0] ^ 1);
	metric[2] = (data[1] ^ 1) + (data[0] ^ 0);
	metric[3] = (data[1] ^ 0) + (data[0] ^ 1);
	metric[4] = (data[1] ^ 1) + (data[0] ^ 1);
	metric[5] = (data[1] ^ 0) + (data[0] ^ 0);
	metric[6] = (data[1] ^ 0) + (data[0] ^ 1);
	metric[7] = (data[1] ^ 1) + (data[0] ^ 0);

	acs(metric);
}

void CSoundCardController::traceBack()
{
	// Start from the S0, t=31
	unsigned int j = 0U;
	unsigned int k = 0U;
	for (int i = 329; i >= 0; i--) {
		switch (j) {
			case 0U: // if state = S0
				if (READ_BIT1(m_pathMemory0, i) == 0)
					j = 0U;
				else
					j = 2U;
				WRITE_BIT1(m_fecOutput, k, 0);
				k++;
				break;


			case 1U: // if state = S1
				if (READ_BIT1(m_pathMemory1, i) == 0)
					j = 0U;
				else
 					j = 2U;
				WRITE_BIT1(m_fecOutput, k, 1);
				k++;
 				break;

			case 2: // if state = S1
				if (READ_BIT1(m_pathMemory2, i) == 0)
 					j = 1U;
				else
					j = 3U;
				WRITE_BIT1(m_fecOutput, k, 0);
				k++;
				break;

			case 3U: // if state = S1
				if (READ_BIT1(m_pathMemory3, i) == 0)
					j = 1U;
				else
					j = 3U;
				WRITE_BIT1(m_fecOutput, k, 1);
				k++;
				break;
		}
	}
}
